package com.tbit.main.util;

import com.tbit.main.exception.BaseException;
import com.tbit.main.pojo.Account;
import com.tbit.main.pojo.Point;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Polygon;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 电子围栏工具类
 *
 * @author Leon
 * 2017年11月30日 上午9:11:14
 */
public class GeoUtil {
    /**
     * 经纬度字符串转Coordinate
     *
     * @param pointString
     * @return
     */
    public static List<Coordinate> getCoordinate(String pointString) {
        List<Coordinate> coordinates = new ArrayList<>();

        String[] pointArray = pointString.split(";");
        for (int i = 0; i < pointArray.length; i++) {
            String[] lonlat = pointArray[i].split(",");
            if (lonlat.length == 2) {
                coordinates.add(new Coordinate(Double.parseDouble(lonlat[0]), Double.parseDouble(lonlat[1])));
            }
        }
        String[] lonlat = pointArray[0].split(",");
        coordinates.add(new Coordinate(Double.parseDouble(lonlat[0]), Double.parseDouble(lonlat[1])));
        return coordinates;
    }

    /**
     * 经纬度字符串转points
     *
     * @param pointString
     * @return
     */
    public static List<Point> getPoints(String pointString) {
        List<Point> points = new ArrayList<>();

        String[] pointArray = pointString.split(";");
        for (int i = 0; i < pointArray.length; i++) {
            String[] lonlat = pointArray[i].split(",");
            if (lonlat.length == 2) {
                points.add(new Point(Double.parseDouble(lonlat[0]), Double.parseDouble(lonlat[1])));
            }
        }

        return points;
    }

    /**
     * Points转字符串
     *
     * @param points
     * @return
     */
    public static String getPointToString(List<Point> points) {
        StringBuilder result = new StringBuilder();
        int size = points.size();
        for (int i = 0; i < size; i++) {
            Point point = points.get(i);
            result.append(point.getX()).append(",").append(point.getY());

            if (i != (size - 1)) {
                result.append(";");
            }
        }

        return result.toString();
    }

    /**
     * 获取电子围栏的中心
     *
     * @param points
     * @return
     */
    public static Point GetCenterPoint(List<Point> points) {
        // 以下为简化方法（400km以内）
        int total = points.size();
        double lat = 0, lon = 0;
        for (Point p : points) {
            lat += p.getX() * Math.PI / 180;
            lon += p.getY() * Math.PI / 180;
        }
        lat /= total;
        lon /= total;
        return new Point(lat * 180 / Math.PI, lon * 180 / Math.PI);
    }

    /**
     * 获取电子围栏的重心点
     *
     * @param mPoints
     * @return
     */
    public static Point getCenterOfGravityPoint(List<Point> mPoints) {
        double area = 0.0;//多边形面积
        double Gx = 0.0, Gy = 0.0;// 重心的x、y
        for (int i = 1; i <= mPoints.size(); i++) {
            double iLat = mPoints.get(i % mPoints.size()).getX();
            double iLng = mPoints.get(i % mPoints.size()).getY();
            double nextLat = mPoints.get(i - 1).getX();
            double nextLng = mPoints.get(i - 1).getY();
            double temp = (iLat * nextLng - iLng * nextLat) / 2.0;
            area += temp;
            Gx += temp * (iLat + nextLat) / 3.0;
            Gy += temp * (iLng + nextLng) / 3.0;
        }
        Gx = Gx / area;
        Gy = Gy / area;
        Double X = Double.valueOf(String.valueOf(Gx).substring(0, String.valueOf(Gx).indexOf(".") + 7));
        Double Y = Double.valueOf(String.valueOf(Gy).substring(0, String.valueOf(Gy).indexOf(".") + 7));
        return new Point(X, Y);
    }

    /**
     * 获取电子围栏的内心点
     *
     * @param CoordinateList
     * @return
     */
    public static Point getCenterOfGravityPoint4(List<Coordinate> CoordinateList) {
        Coordinate[] coordinates = new Coordinate[CoordinateList.size()];
        CoordinateList.toArray(coordinates);
        GeometryFactory geometryFactory = new GeometryFactory();
        Polygon p = geometryFactory.createPolygon(coordinates);//创建多边形
        org.locationtech.jts.geom.Point pt = p.getInteriorPoint();//多边形内心
        return new Point(pt.getX(), pt.getY());
    }

    private static double getDis(Point a, Point b) {
        double x = a.getX() - b.getX();
        double y = a.getY() - b.getY();
        return Math.sqrt(x * x + y * y);
    }

    /**
     * 返回c点到线段ab的最短距离
     *
     * @param c
     * @param a
     * @param b
     * @return
     */
    public static double pt2LineDist(Point c, Point a, Point b) {
        Point ab = new Point(b.getX() - a.getX(), b.getY() - a.getY());
        Point ac = new Point(c.getX() - a.getX(), c.getY() - a.getY());

        double r = ab.getX() * ac.getX() + ab.getY() * ac.getY();
        r /= getDis(a, b) * getDis(a, b);
        if (Double.isNaN(r)) {
            return Double.MAX_VALUE;
        }
        // 如果投影点在A外，则距离是AC之间距离
        if (r < 0) {
            return getDistance(a, c);
        }

        // 如果投影点在B外，则距离是BC之间距离
        if (r > 1) {
            return getDistance(b, c);
        }

        // 如果投影在AB线段上，则距离是C到投影点之间距离
        Point d = new Point(ab.getX(), ab.getY());
        d.setX(d.getX() * r);
        d.setY(d.getY() * r);
        d.setX(d.getX() + a.getX());
        d.setY(d.getY() + a.getY());
        return getDistance(c, d);
    }

    /***
     * 获取点到多边形的距离
     * @param c
     * @param points
     * @return
     */
    public static Double getDistance(Point c, List<Point> points) {
        /** 最近的边 */
        Double distanceShort = null;
        Integer len = points.size();
        for (int i = 0; i < len; i++) {
            // 多边形中当前点
            Point a = points.get(i % points.size());
            Point b = points.get((i + 1) % points.size());
            double l = pt2LineDist(c, a, b);
            if (distanceShort == null) {
                distanceShort = l;
            } else {
                if (l < distanceShort) {
                    distanceShort = l;
                }
            }
        }
        return distanceShort;
    }

    /**
     * 检测点是否在多边形内
     *
     * @param point
     * @param points
     * @return
     */
    public static boolean IsPtInPoly(Point point, List<Point> points) {
        boolean flag = false;
        int n = points.size();
        Point dp = point;
        Point[] dps = new Point[n];
        points.toArray(dps);
        for (int i = 0; i < n; i++) {
            if (dp.getY() < dps[i].getY() && dp.getY() < dps[(i + 1) % n].getY()) {
                continue;
            }
            if (dps[i].getX() <= dp.getX() && dps[(i + 1) % n].getX() <= dp.getX()) {
                continue;
            }
            double dx = dps[(i + 1) % n].getX() - dps[i].getX();
            double dy = dps[(i + 1) % n].getY() - dps[i].getY();
            double t = (dp.getX() - dps[i].getX()) / dx;
            double y = t * dy + dps[i].getY();
            if (y <= dp.getY() && t >= 0 && t <= 1) {
                flag = !flag;
            }
        }
        return flag;
    }

    /**
     * 获取两点之间的距离
     */
    public static double getDistance(Point p1, Point p2) {
        double lon1 = (Math.PI / 180) * p1.getX();
        double lon2 = (Math.PI / 180) * p2.getX();
        double lat1 = (Math.PI / 180) * p1.getY();
        double lat2 = (Math.PI / 180) * p2.getY();

        // 地球半径
        double R = 6371;
        double d = Math.acos(Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1))
                * R;
        //转换成千米（根据自己需求）

        return d * 1000;
    }

    /**
     * 获取多边形面积
     *
     * @param points
     * @return
     */
    public static Integer pointArea(List<Point> points) {
        double S = points.get(0).getY() * (points.get(points.size() - 1).getX() - points.get(1).getX());
        for (int m = 1; m < points.size(); m++) {
            S += points.get(m).getY() * (points.get(m - 1).getX() - points.get((m + 1) % (points.size())).getX());
        }
        return (int) (Math.round(((Math.abs(S / 2) * 9101160000.085981))));
    }

    /* 获取到运营区域的品牌*/
    public static Integer getBrandConfigOracleJob(Integer accountId, Map<Integer, Account> listAccount, Integer time) {
        time++;
        if (time.equals(4)) {
            throw new BaseException("Recursion.Error");
        }
        Account account = listAccount.get(accountId);
        if (null == account) {
            throw new RuntimeException("错误");
        }
        if (account.getType().equals(1)) {
            return account.getAccountId();
        }
        return getBrandConfigOracleJob(account.getParentId(), listAccount, time);
    }

    /* 获取到运营区域的品牌*/
    public static Integer getBrandConfigListOracleJob(Integer accountId, List<Account> listAccount) {
        Account account = listAccount.stream().filter(e -> e.getAccountId().equals(accountId)).findFirst().orElseThrow(RuntimeException::new);
        if (account.getType().equals(1)) {
            return account.getAccountId();
        }
        return getBrandConfigListOracleJob(account.getParentId(), listAccount);
    }
}
